﻿using System;
using System.Net;
using System.Windows;
using Windows.Foundation;
using Windows.UI.Xaml;

namespace BlueRoseGames.Controls.Text
{
    public sealed class PerspectiveTransform : Transform
    {
        double _cameraX = 0;
        double _cameraY = 0;
        double _cameraZ = 120;
        double _angleX = 0;
        double _angleY = 0;
        double _angleZ = 0;

        #region DependencyProperties

        public static readonly DependencyProperty CameraXProperty = DependencyProperty.Register("CameraX", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(0d, OnCameraXChanged));
        public double CameraX
        {
            get { return (double)GetValue(CameraXProperty); }
            set { SetValue(CameraXProperty, value); }
        }

        public static readonly DependencyProperty CameraYProperty = DependencyProperty.Register("CameraY", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(0d, OnCameraYChanged));
        public double CameraY
        {
            get { return (double)GetValue(CameraYProperty); }
            set { SetValue(CameraYProperty, value); }
        }

        public static readonly DependencyProperty CameraZProperty = DependencyProperty.Register("CameraZ", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(120d, OnCameraZChanged));
        public double CameraZ
        {
            get { return (double)GetValue(CameraZProperty); }
            set { SetValue(CameraZProperty, value); }
        }

        public static readonly DependencyProperty AngleXProperty = DependencyProperty.Register("AngleX", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(0d, OnAngleXChanged));
        public double AngleX
        {
            get { return (double)GetValue(AngleXProperty); }
            set { SetValue(AngleXProperty, value); }
        }

        public static readonly DependencyProperty AngleYProperty = DependencyProperty.Register("AngleY", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(0d, OnAngleYChanged));
        public double AngleY
        {
            get { return (double)GetValue(AngleYProperty); }
            set { SetValue(AngleYProperty, value); }
        }

        public static readonly DependencyProperty AngleZProperty = DependencyProperty.Register("AngleZ", typeof(double), typeof(PerspectiveTransform), new PropertyMetadata(0d, OnAngleZChanged));
        public double AngleZ
        {
            get { return (double)GetValue(AngleZProperty); }
            set { SetValue(AngleZProperty, value); }
        }

        private static void OnCameraXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._cameraX = (double)e.NewValue;
            pt.Changed = true;
        }

        private static void OnCameraYChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._cameraY = (double)e.NewValue;
            pt.Changed = true;
        }

        private static void OnCameraZChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._cameraZ = (double)e.NewValue;
            pt.Changed = true;
        }

        private static void OnAngleXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._angleX = ((double)e.NewValue).ToRadians();
            pt.Changed = true;
        }

        private static void OnAngleYChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._angleY = ((double)e.NewValue).ToRadians();
            pt.Changed = true;
        }

        private static void OnAngleZChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var pt = (PerspectiveTransform)dependencyObject;
            pt._angleZ = ((double)e.NewValue).ToRadians();
            pt.Changed = true;
        }
        
        #endregion


        public override Point TransformPoint(Point p)
        {
            // Algorithm adapted from http://en.wikipedia.org/wiki/3D_projection

            double sinX = Math.Sin(_angleX);
            double cosX = Math.Cos(_angleX);
            double sinY = Math.Sin(_angleY);
            double cosY = Math.Cos(_angleY);
            double sinZ = Math.Sin(_angleZ);
            double cosZ = Math.Cos(_angleZ);

            double cameraX = this._cameraX;
            double cameraY = this._cameraY;
            double cameraZ = this._cameraZ;

            double offsetX = cameraX - p.X;
            double offsetY = cameraY - p.Y;
            double offsetZ = cameraZ;

            double aux1 = cosY * offsetZ + sinY * (sinZ * offsetY + cosZ * offsetX);
            double aux2 = cosZ * offsetY - sinZ * offsetX;

            double dX = cosY * (sinZ * offsetY + cosZ * offsetX) - sinY * offsetZ;
            double dY = sinX * aux1 + cosX * aux2;
            double dZ = cosX * aux1 - sinX * aux2;

            double w = cameraZ / dZ;

            Point ret = new Point();
            ret.X = cameraX - dX * w;
            ret.Y = cameraY - dY * w;
            return ret;
        }
    }

    public static class DoubleUtil
    {
        public static double ToRadians(this double degrees)
        {
            return (degrees * Math.PI) / 180d;
        }
    }
}
